﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using CalcSharp.Core.Containers;
using ICSharpCode.SharpZipLib.Zip;

namespace CalcSharp.Core.Memory
{
    public class MemoryDumpReader
    {
        private ZipFile Zip;
        private FileStream ZFile;
        private string Fpath;
        private Version thisVer;
        private MemoryDumpInfo dmp;

        private Dictionary<string, int>  Float;
        private Dictionary<string, int> Complex;
        private Dictionary<string, int> Matrix;
        private Dictionary<string, int> Set;
        private Dictionary<string, int> String;
        private Dictionary<string, int> Function;
        private Dictionary<string, int> Misc;

        public MemoryDumpReader(string Filepath)
        {
            thisVer = new Version(1,0,0,0);
            if (File.Exists(Filepath))
            {
                this.ZFile = File.OpenRead(Filepath);
                this.Fpath = Filepath;
                this.Zip = new ZipFile(this.ZFile);
                this.Float = new Dictionary<string, int>();
                this.Complex = new Dictionary<string, int>();
                this.Matrix = new Dictionary<string, int>();
                this.Set = new Dictionary<string, int>();
                this.String = new Dictionary<string, int>();
                this.Function = new Dictionary<string, int>();
                this.Misc = new Dictionary<string, int>();
                string file;

                for (int i = 0; i < this.Zip.Count; i++)
                {
                    file = Zip[i].Name.ToLower();
                    if (file.StartsWith("float/")) this.Float.Add(file, i);
                    else if (file.StartsWith("cplx/")) this.Complex.Add(file, i);
                    else if (file.StartsWith("mtrx/")) this.Matrix.Add(file, i);
                    else if (file.StartsWith("set/")) this.Set.Add(file, i);
                    else if (file.StartsWith("string/")) this.String.Add(file, i);
                    else if (file.StartsWith("function/")) this.Function.Add(file, i);
                    else Misc.Add(file, i);
                }
                dmp = ReadFormatInfo();
                if (dmp.Version != thisVer) throw new CSException("Unsuported dump format");
                this.Zip.Close();
            }
            else throw new CSException("File doesn't exist: " + Filepath);
        }

        public bool DataExists(string dataname)
        {
            dataname = dataname.ToLower();
            if (Float.ContainsKey(dataname)) return true;
            else if (Complex.ContainsKey(dataname)) return true;
            else if (Matrix.ContainsKey(dataname)) return true;
            else if (Set.ContainsKey(dataname)) return true;
            else if (String.ContainsKey(dataname)) return true;
            else if (Function.ContainsKey(dataname)) return true;
            else return false;
        }

        private MemoryDumpInfo ReadFormatInfo()
        {
            MemoryDumpInfo ret;
            if (Misc.ContainsKey("dump.bin"))
            {
                Stream s = Zip.GetInputStream(Misc["dump.bin"]);
                BinaryFormatter bf = new BinaryFormatter();
                ret = (MemoryDumpInfo)bf.Deserialize(s);
                return ret;
            }
            else throw new CSException("Unsuported memory dump file");
        }

        private int ReturnIndexOf(string dataname)
        {
            dataname = dataname.ToLower();
            if (Float.ContainsKey(dataname)) return Float[dataname];
            else if (Complex.ContainsKey(dataname)) return Complex[dataname];
            else if (Matrix.ContainsKey(dataname)) return Matrix[dataname];
            else if (Set.ContainsKey(dataname)) return Set[dataname];
            else if (String.ContainsKey(dataname)) return String[dataname];
            else if (Function.ContainsKey(dataname)) return Function[dataname];
            else return -1;
        }

        public string[] GetVariablesOfType(DumpDataType t)
        {
            switch (t)
            {
                case DumpDataType.Float:
                    return this.Float.Keys.ToArray();
                case DumpDataType.Complex:
                    return this.Complex.Keys.ToArray();
                case DumpDataType.Set:
                    return this.Set.Keys.ToArray();
                case DumpDataType.Matrix:
                    return this.Matrix.Keys.ToArray();
                case DumpDataType.String:
                    return this.String.Keys.ToArray();
                case DumpDataType.Function:
                    return this.Function.Keys.ToArray();
                default:
                    return null;
            }
        }

        public object Deserialize(string dataname)
        {
            this.ZFile = File.OpenRead(this.Fpath);
            
            if (DataExists(dataname))
            {
                using (this.Zip = new ZipFile(this.ZFile))
                {
                    Stream s = Zip.GetInputStream(ReturnIndexOf(dataname));
                    BinaryFormatter bf = new BinaryFormatter();
                    return bf.Deserialize(s);
                }
            }
            else throw new CSException("Dump doesn't contain: "+dataname);
        }

        public void DeserializeAllMem(ref MatrixManager mt, ref SetManager st, ref FloatManager ft, ref StringManager str, ref ComplexManager cplx, ref FunctionManager fnc)
        {
            mt.Clear();
            st.Clear();
            ft.Clear();
            str.Clear();
            cplx.Clear();
            fnc.Clear();

            mt.StorageType = dmp.MatrixStorage;
            st.StorageMode = dmp.SetStorage;

            foreach (string key in Float.Keys) ft.Add((Data)Deserialize(key));
            foreach (string key in Complex.Keys) cplx.Add((CplxData)Deserialize(key));
            foreach (string key in Matrix.Keys) mt.Add((MatrixData)Deserialize(key));
            foreach (string key in Set.Keys) st.Add((SetData)Deserialize(key));
            foreach (string key in String.Keys) str.Add((StringData)Deserialize(key));
            foreach (string key in Function.Keys) fnc.Add((FunctData)Deserialize(key));
        }
    }
}
